package org.javahispano.javacup.tacticas_aceptadas.kaprichodelsur.jmv;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

import org.javahispano.javacup.modelo.Comando;
import org.javahispano.javacup.modelo.ComandoIrA;
import org.javahispano.javacup.modelo.Constantes;
import org.javahispano.javacup.modelo.JugadorDetalle;
import org.javahispano.javacup.modelo.Posicion;
import org.javahispano.javacup.modelo.SituacionPartido;

/**
 * Clase de utilidad para la t�ctica del betis.
 * 
 * @author Juanma
 */
public class TacticaJmvCfUtils {

	// Obtengo la posicion del balon respecto a las zonas de juego de mi
	// equipo (defensa, mediocampo, delantera)
	public static final double ZONA_DEFENSA = -15;
	public static final double ZONA_DELANTERA = 15;
	
	/**
	 * Zona del campo de juego.
	 * @author Juanma
	 */
	public enum ZonaCampo{
		DEFENSA,
		MEDIOCAMPO,
		DELANTERA
	}
	
	/**
	 * Es la distancia que utilizamos para saber si un jugador tiene un rival cerca.
	 */
	public static final double DISTANCIA_MARCAJE = 5;
	
	private TacticaJmvCfUtils() {
		// Clase privada para utilizarla como utilidad.
	}

	public static int obtenerIndicePortero(SituacionPartido sp) {

		int result = -1;

		int indicePortero = 0;
		for (JugadorDetalle jugador : sp.detalleMisJugadores()) {
			if (jugador.esPortero()) {
				result = indicePortero;
				break;
			}
			indicePortero++;
		}

		return result;
	}

	/**
	 * Devuelve TRUE si alg�n jugador de nuestro equipo est� m�s cerca del bal�n
	 * que cualquier jugador contrario.
	 * 
	 * @param sp
	 * @return boolean
	 */
	public static boolean esPosibleObtenerBalon(SituacionPartido sp) {

		boolean result = false;

		double distanciaMinimaContrario = distanciaMinimaBalon(sp.rivales(), sp
				.balon());
		double distanciaMinimaMiJugador = distanciaMinimaBalon(sp
				.misJugadores(), sp.balon());

		if (distanciaMinimaMiJugador < distanciaMinimaContrario) {
			result = true;
		}

		return result;
	}

	/**
	 * Devuelve la distancia m�nima respecto a la posici�n del bal�n de todas
	 * las posiciones de los jugadores que se le pasan.
	 * 
	 * @param posicionesJugadores
	 *            Posicion[]
	 * @param posicionBalon
	 *            Posicion
	 * @return double
	 */
	private static double distanciaMinimaBalon(Posicion[] posicionesJugadores,
			Posicion posicionBalon) {

		double result = -1;

		for (Posicion posicionRival : posicionesJugadores) {

			double distancia = posicionBalon.distancia(posicionRival);
			if (result == -1) {
				result = distancia;
			} else if (distancia < result) {
				result = distancia;
			}
		}

		return result;
	}

	/**
	 * Devuelve un array con los �ndices de los jugadores que est�n libre de
	 * marca.
	 * 
	 * @param sp
	 * @return
	 */
	public static Integer[] jugadoresSinMarca(SituacionPartido sp) {

		List<Integer> indiceJugadores = new ArrayList<Integer>();

		int indice = 0;
		for (Posicion miJugad : sp.misJugadores()) {

			int rivalMasCercano = miJugad.indiceMasCercano(sp.rivales());
			if (miJugad.distancia(sp.rivales()[rivalMasCercano]) > DISTANCIA_MARCAJE) {
				indiceJugadores.add(indice);
			}

			indice++;
		}
		
		// Pasamos la lista a array.
		int size = indiceJugadores.size();
		Integer[] result = new Integer[size];
		int i = 0;
		for(Integer ind : indiceJugadores){
			result[i] = ind;
			i++;
		}

		return result;
	}
	
	/**
	 * Devuelve un array con los �ndices de los jugadores que est�n libre de
	 * marca.
	 * 
	 * @param sp
	 * @return
	 */
	public static boolean jugadorSinMarca(SituacionPartido sp, int jugador) {

		boolean result = false;

		Object[] jugadoresSinMarca = jugadoresSinMarca(sp);
		
		if(Arrays.asList(jugadoresSinMarca).contains(jugador)){
			result = true;
		}
		
		return result;
	}

	/**
	 * Devuelve el indice del jugador mas cercano sin marca y sin un jugador en
	 * la trayectoria para el pase del bal�n.
	 * 
	 * @param sp
	 * @return
	 */
	public static int jugadorSinMarcaMasCercano(SituacionPartido sp,
			Posicion jugadorConBalon) {

		int result = -1;

		Integer[] sinMarca = jugadoresSinMarca(sp);
		
		int indice = 0;
		double distanciaMasCerca = -1;
		for (Posicion miJugad : sp.misJugadores()) {

			if (jugadorConBalon.equals(miJugad)
					|| !Arrays.asList(sinMarca).contains(indice)) {
				indice++;
				continue;
			}

			// System.out.println("Distancia al rival mas cercano: " +
			// distanciaRivalMasCercano + ", Jugador n�mero: " +
			// sp.detalleMisJugadores()[indice].getNumero());
			double distanciaAJugConBalon = miJugad.distancia(jugadorConBalon);
			// System.out.println("Distancia al jugador con balon: " +
			// distanciaAJugConBalon + ", Jugador n�mero: " +
			// sp.detalleMisJugadores()[indice].getNumero());
			boolean pasarBalon = esPosiblePasarBalon(sp.rivales(),
					jugadorConBalon, miJugad);
			pasarBalon = true; // TODO quitar
			if ((distanciaMasCerca == -1 || distanciaAJugConBalon < distanciaMasCerca)
					&& pasarBalon) {
				distanciaMasCerca = miJugad.distancia(jugadorConBalon);
				result = indice;
			}
		}

		indice++;

		return result;
	}
	
	/**
	 * Devuelve el indice del jugador mas cercano sin marca y sin un jugador en
	 * la trayectoria para el pase del bal�n.
	 * 
	 * @param sp
	 * @return
	 */
	public static int jugadorSinMarcaAleatorio(SituacionPartido sp,
			Posicion jugadorConBalon, ZonaCampo zona) {

		int result = -1;

		List<Integer> jugadoresZona = obtenerJugadoresSegunZona(zona, sp);
		Integer[] sinMarca = jugadoresSinMarca(sp);
		List<Integer> jugadPosibles = new ArrayList<Integer>();
		
		for(Integer jugadorZona : jugadoresZona){
			
			if(!Arrays.asList(sinMarca).contains(jugadorZona)){
				continue;
			}
			
			jugadPosibles.add(jugadorZona);
		}
		
		if(jugadPosibles.size() == 0){
			result = -1;
			return result;
		}
		
		Random rnd = new Random(new Date().getTime());
		result = rnd.nextInt(jugadPosibles.size());
		
		return jugadPosibles.get(result);
	}

	/**
	 * Devuelve TRUE si no hay ning�n rival por medio para pasar el bal�n a un
	 * compa�ero.
	 * 
	 * @param rivales
	 * @param jugadorConBalon
	 * @param jugadorDestino
	 * @return
	 */
	public static boolean esPosiblePasarBalon(Posicion[] rivales,
			Posicion jugadorConBalon, Posicion jugadorDestino) {

		boolean result = true;

		for (Posicion rival : rivales) {

			Posicion interseccion = Posicion.Interseccion(jugadorConBalon,
					jugadorDestino, rival, rival);

			if (interseccion != null) {
				// Hay un jugador que impide el pase
				return false;
			}
		}

		return result;
	}
	
	/**
	 * Mueve a los jugadores de la zona que se le pasa, para quitarse de la marca del rival.
	 * @param zona
	 * @param sp
	 */
	public static List<Comando> desmarcarJugadores(ZonaCampo zona, SituacionPartido sp){
		
		List<Comando> result = new ArrayList<Comando>();
		
		Posicion[] misJugadores = sp.misJugadores();
		if(zona == ZonaCampo.DELANTERA){
			// Obtenemos todos los jugadores que est�n en la zona de la delantera
			List<Integer> jugadDelanteros = new ArrayList<Integer>();
			int indice = 0;
			if (misJugadores != null) {
				for (Posicion posJugador : misJugadores) {
					if (posJugador != null && posJugador.getY() > ZONA_DELANTERA) {
						jugadDelanteros.add(indice);
					}
					indice++;
				}
			}
			
			// Comprobamos si un jugador NO tiene el bal�n para que se libre de su marcaje
			for(int delantero : jugadDelanteros){
				int[] puedenRematar = sp.puedenRematar();
				
				if(!Arrays.asList(puedenRematar).contains(delantero) && !TacticaJmvCfUtils.jugadorSinMarca(sp, delantero)){
					// El jugador no puede rematar y NO esta libre de marcaje
					// Movemos al jugador 8 metros mas cerca del bal�n.
					
					Posicion posDelantero = sp.misJugadores()[delantero];
					
					double xMover = posDelantero.getX();
					double yMover = posDelantero.getY();
					if(sp.balon().getX() > posDelantero.getX()){
						xMover += TacticaJmvCfUtils.DISTANCIA_MARCAJE; 
					}else{
						xMover -= TacticaJmvCfUtils.DISTANCIA_MARCAJE;
					}
					
					if(sp.balon().getY() > posDelantero.getY()){
						yMover += TacticaJmvCfUtils.DISTANCIA_MARCAJE; 
					}else{
						yMover -= TacticaJmvCfUtils.DISTANCIA_MARCAJE;
					}
					
					posDelantero = sp.misJugadores()[delantero].moverPosicion(xMover, yMover, TacticaJmvCfUtils.DISTANCIA_MARCAJE);
					
					result.add(new ComandoIrA(delantero,posDelantero));
				}
			}
		}
		
		return result;
		
	}
	
	/**
	 * Devuelve los jugadores seg�n la zona.
	 * @return
	 */
	public static List<Integer> obtenerJugadoresSegunZona(ZonaCampo zona, SituacionPartido sp){
		
		List<Integer> result = new ArrayList<Integer>();
		
		Posicion[] misJugadores = sp.misJugadores();
		if(zona == ZonaCampo.DEFENSA){
			int indice = 0;
			if (misJugadores != null) {
				for (Posicion posJugador : misJugadores) {
					if (posJugador != null && posJugador.getY() < TacticaJmvCfUtils.ZONA_DEFENSA) {
						result.add(indice);
					}
					indice++;
				}
			}
		}else if(zona == ZonaCampo.MEDIOCAMPO){
			int indice = 0;
			if (misJugadores != null) {
				for (Posicion posJugador : misJugadores) {
					if (posJugador != null && posJugador.getY() <= TacticaJmvCfUtils.ZONA_DELANTERA && posJugador.getY() >= TacticaJmvCfUtils.ZONA_DEFENSA) {
						result.add(indice);
					}
					indice++;
				}
			}
		}else if(zona == ZonaCampo.DELANTERA){
			int indice = 0;
			if (misJugadores != null) {
				for (Posicion posJugador : misJugadores) {
					if (posJugador != null && posJugador.getY() > TacticaJmvCfUtils.ZONA_DELANTERA) {
						result.add(indice);
					}
					indice++;
				}
			}
		}
		
		return result;
		
	}
	
	/**
	 * Devuelve TRUE si el jugador que se le pasa tiene un rival cerca
	 * @param sp
	 * @return
	 */
	public static boolean tieneRivalCerca(SituacionPartido sp, Posicion posJugador){
		
		boolean result = false;
		
		int rivalMasCercano = posJugador.indiceMasCercano(sp.rivales());
		
		if(posJugador.distancia(sp.rivales()[rivalMasCercano]) <= DISTANCIA_MARCAJE){
			result = true;
		}
		
		return result;
		
	}
	
	/**
	 * Devuelve TRUE si el jugador que se le pasa tiene un rival cerca
	 * @param sp
	 * @return
	 */
	public static boolean tieneRivalCerca(SituacionPartido sp, int indiceJugador){
		
		boolean result = false;
		
		Posicion posJugador = sp.misJugadores()[indiceJugador];
		
		int rivalMasCercano = posJugador.indiceMasCercano(sp.rivales());
		
		if(posJugador.distancia(sp.rivales()[rivalMasCercano]) <= DISTANCIA_MARCAJE){
			result = true;
		}
		
		return result;
		
	}
	
}
